using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
using System.Linq;

using Prota.Unity;

namespace Prota.Editor
{
    [InitializeOnLoad]
    public class EnhancedHierarchy : UnityEditor.Editor
    {
        static EnhancedHierarchy()
        {
            UpdateSettings();
        }
        
        static EditorPrefEntryBool registered = new("Prota:EnhancedHierarchyEnabled");
        
        [MenuItem("ProtaFramework/Functionality/Toggle Enhanced Hierarchy", priority = 2250)]
        static void SwitchEnhancedHierarchy()
        {
            registered.value = !registered.value;
            Menu.SetChecked("ProtaFramework/Functionality/Toggle Enhanced Hierarchy", registered.value);
            UpdateSettings();
        }
        
        static EditorPrefEntryBool showZ = new("Prota:ShowZCoordinates");
        [MenuItem("ProtaFramework/Functionality/Show Z Coordinates", priority = 2251)]
        static void SwitchShowZ()
        {
            showZ.value = !showZ.value;
            Menu.SetChecked("ProtaFramework/Functionality/Show Z Coordinates", showZ.value);
            UpdateSettings();
        }
        
        
        [MenuItem("ProtaFramework/GC Unused Assets", priority = 10000)]
        static void GCUnusedAssets()
        {
            Resources.UnloadUnusedAssets();
        }
        
        
        static void UpdateSettings()
        {
            EditorApplication.hierarchyWindowItemOnGUI -= OnHierarchyGUI;
            if(registered.value) EditorApplication.hierarchyWindowItemOnGUI += OnHierarchyGUI;
            Menu.SetChecked("ProtaFramework/Functionality/Toggle Enhanced Hierarchy", registered.value);
            Menu.SetChecked("ProtaFramework/Functionality/Show Z Coordinates", showZ.value);
        }
        
        private const int pixelPerDepth = 14;
        static readonly List<Component> comps = new List<Component>();
        
        const int maxIconCount = 10;
        
        
        static void OnHierarchyGUI(int instanceId, Rect area)
        {
            var target = EditorUtility.InstanceIDToObject(instanceId);
            
            if(target is not GameObject g) return;
            
            var originalGUIColor = GUI.color;
            
            var rightMargin = area.xMax - 20;
            var space = area.height - 4;                // 每个图标向左偏移多少像素.
            
            var select = Selection.activeGameObject.CheckNull();
            var selectTr = select?.transform;
            var curSelectedERoot = selectTr?.GetComponentInParent<ERoot>(true);
            
            var depth = g.transform.GetDepth();
            var gtr = g.transform;
            
            if(showZ.value)
            {
                var op = selectTr == null ? ""
                    : gtr.position.z > selectTr.position.z ? ">"
                    : gtr.position.z < selectTr.position.z ? "<"
                    : "=";
                var style = op == "" ? coordEqualStyle
                    : op == "<" ? coordLargerStyle
                    : op == ">" ? coordLowerStyle
                    : coordEqualStyle;
                
                var loc = Rect.MinMaxRect(area.xMax - 70, area.yMin, area.xMax - 10, area.yMax);
                GUI.Label(loc, $"{gtr.position.z:F2}", style.value);
                loc = Rect.MinMaxRect(area.xMax - 80, area.yMin, area.xMax - 70, area.yMax);
                GUI.Label(loc, op, style.value);
                rightMargin -= 80;
            }
            
            // SetActive 部分.
            var active = EditorGUI.Toggle(new Rect(rightMargin, area.yMax - area.height, 16, 16), g.activeSelf);
            rightMargin -= space + 2;
            if(active != g.activeSelf)
            {
                Undo.RecordObject(g, "Activation");
                g.SetActive(active);
                Selection.activeObject = g;
            }
            
            // 这个 gameobject 下属的 gameobject.
            g.GetComponents(comps);
            comps.RemoveAll(x => x == null || x is Transform);
            if(comps.Any(x => x is ProceduralMesh || x is MeshRenderer))
            {
                comps.RemoveAll(x => x is MeshFilter);
            }
            comps.Sort(Compare);
            // comps.Reverse();
            
            // Component 图标部分.
            int n = 0;
            bool entityDrawed = false;
            foreach(var c in comps)
            {
                GUIContent r = null;
                n++;
                if(n > maxIconCount) break;
                
                if(c is EComponent)
                {
                    if(entityDrawed) continue;
                    entityDrawed = true;
                    bool current = curSelectedERoot != null
                        && g.TryGetComponentInParent<ERoot>(out var eroot)
                        && curSelectedERoot == eroot;
                    r = new GUIContent(current ? ecompTextureActivated.value : ecompTexture.value);
                }
                
                if(c is ERoot)
                {
                    bool current = curSelectedERoot != null
                        && g.TryGetComponentInParent<ERoot>(out var eroot)
                        && curSelectedERoot == eroot;
                    r = new GUIContent(current ? erootTextureActivated.value : erootTexture.value);
                }
                
                if(r == null) r = c.FindEditorIconGUIContent();
                
                rightMargin -= 1;
                GUI.Label(new Rect(rightMargin, area.yMax - area.height, 16, 16), r);
                rightMargin -= space;
            }
            
            
            // Canvas 标记部分. 如果一个物体被挂在 canvas 下方则有一个蓝色竖线标记.
            if(g.GetComponentInParent<Canvas>() != null)
            {
                var r = new Rect(area);
                r.xMin -= 20 + depth * pixelPerDepth;
                r.xMax = r.xMin + r.height;
                GUI.color = new Color(0.6f, 0.65f, 1, 1f);
                GUI.DrawTexture(r, barTexture.value);
            }
            
            // 全局单例标记部分, 命名带 # 号, 或者组件带有 Singleton.
            {
                bool hasSingleton = false;
                foreach(var c in comps)
                {
                    var t = c.GetType();
                    if(!t.IsGenericType) continue;
                    t = t.GetGenericTypeDefinition();
                    if(t != typeof(Singleton<>)) continue;
                    hasSingleton = true;
                    break;
                }
                if(g.name.StartsWith("#") || hasSingleton)
                {
                    var r = new Rect(area);
                    r.xMin -= 22 + depth * pixelPerDepth;
                    r.xMax = r.xMin + r.height;
                    GUI.color = new Color(0.6f, 0.0f, 0.0f, 1f);
                    GUI.DrawTexture(r, barTexture.value);
                }
            }
                
            // 标记名称前缀为 >>> 或 ===, 后缀为 <<< 或 === 的物体.
            var name = g.name;
            if((name.StartsWith(">>>") || name.StartsWith("===") || name.StartsWith("---"))
            && (name.EndsWith("<<<") || name.EndsWith("===") || name.EndsWith("---")))
            {
                var r = new Rect(area);
                // r.xMin -= depth * pixelPerDepth;
                r.xMin += 20;
                var oriMax = r.yMax;
                r.yMax = r.yMin + 1;
                
                GUI.color = new Color(0.6f, 0.6f, 0.6f, 1f);
                GUI.DrawTexture(r, backTexture.value);
            }
            
            // 标记当前选择的 GameObject 的 DataBinding.
            if(Selection.activeGameObject != null
                && Selection.activeGameObject.TryGetComponent<DataBinding>(out var dataBinding))
            {
                var list = (SerializableDictionary<string, GameObject>)dataBinding.ProtaReflection().Get("data");
                if(list.Any(x => x.Value == g))
                {
                    var r = new Rect(area);
                    r.xMin = r.xMax;
                    r.xMax = r.xMin + 2;
                    GUI.color = new Color(1f, 0.6f, 0.6f, 1f);
                    GUI.DrawTexture(r, backTexture.value);
                }
            }
            
            GUI.color = originalGUIColor;
        }
        
        static int GetWeight(Component x)
        {
            // if(typeof(EComponent).IsAssignableFrom(x.GetType())) return 99;
            switch(x)
            {
                case ERoot: return 100;
                case EComponent: return 99;
                
                case Rigidbody: return 90;
                case PhysicsContactRecorder3D: return 89;
                case Collider: return 88;
                
                case Rigidbody2D: return 90;
                case PhysicsContactRecorder2D: return 89;
                case Collider2D: return 88;
                
                case DataBinding: return 10;
                
            }
            
            return 0;
        }
        
        static int Compare(Component a, Component b)
        {
            // reversed.
            return GetWeight(b).CompareTo(GetWeight(a));
            // return GetWeight(a).CompareTo(GetWeight(b));
        }
    
        // ====================================================================================================
        // ====================================================================================================
        
        
        static CachedValue<Texture2D> barTexture = new(
            () => Resources.Load<Texture2D>("ProtaFramework/line_vertical_16_2"), true
        );
        static CachedValue<Texture2D> backTexture = new(
            () => Resources.Load<Texture2D>("ProtaFramework/rect_16"), true
        );
        
        static CachedValue<Texture2D> ecompTexture = new(
            () => Resources.Load<Texture2D>("ProtaFramework/icon_ecomponent_1"), true
        );
        static CachedValue<Texture2D> ecompTextureActivated = new(
            () => Resources.Load<Texture2D>("ProtaFramework/icon_ecomponent_2"), true
        );
        static CachedValue<Texture2D> erootTexture = new(
            () => Resources.Load<Texture2D>("ProtaFramework/icon_eroot_1"), true
        );
        static CachedValue<Texture2D> erootTextureActivated = new(
            () => Resources.Load<Texture2D>("ProtaFramework/icon_eroot_2"), true
        );
        static CachedValue<GUIStyle> coordEqualStyle = new(
            () => {
                var s = new GUIStyle();
                s.normal.textColor = new Color(.8f, .8f, .9f, 1f);
                s.alignment = TextAnchor.MiddleRight;
                return s;
            }
        );
        static CachedValue<GUIStyle> coordLowerStyle = new(
            () => {
                var s = new GUIStyle();
                s.normal.textColor = new Color(0.8f, 0.4f, 1f, 1f);
                s.alignment = TextAnchor.MiddleRight;
                return s;
            }
        );
        static CachedValue<GUIStyle> coordLargerStyle = new(
            () => {
                var s = new GUIStyle();
                s.normal.textColor = new Color(0.4f, 0.8f, 1f, 1f);
                s.alignment = TextAnchor.MiddleRight;
                return s;
            }
        );
    
    }
}
